/*-------------------------------------------------------- 
* Module Name : OLFlickAndDrag
* Version : 1.0
* 
* Software Name : Qt App Template
* Version : 1.0
*
* Copyright � 2010 � 2011 France T�l�com
* This software is distributed under the LGPL v2.1 license,
* the text of which is available at http://www.gnu.org/licenses/lgpl-2.1.html
* or see the "LICENSE.txt" file for more details.
*
*--------------------------------------------------------
* File Name   : OLFlickAndDrag.h
*
* Created     : 07 June 2011
* Author(s)   : Omer Saleem
*
* Description :
* Utility object that provides vertical kinetic scrolling
* for any generic scrollable object such as QScrollArea.
*
*--------------------------------------------------------
* 1.0 - First open source release
*
*/

#ifndef __OLFLICKANDDRAG_H__
#define __OLFLICKANDDRAG_H__

#include <QPoint>
#include <QObject>
#include <QTime>

class QTimer;

/*
 This class can be used to provide kinetic scrolling. Only supports vertical scrolling and left/right
 swiping.

 USAGE:
 Derive from OLFlickAndDragInfoProvider and implement the functions. The scrollValue() and setScrollValue()
 are the most important. These can be used with for example the QScrollBar class from the QScrollableArea
 class. The other cllback functions are for when an event/gesture is detected.

 Create the OLFlickAndDrag class and pass the min and max scroll values (eg. from QScrollBar) and the observer
 described above. The mouse events then need to be fed into the handleMousePress/Move/Release functions ie...

    void MyClass::mousePressEvent(QMouseEvent* event)
    {
        if (event->button() == Qt::LeftButton)
            m_flickable->handleMousePress(event->pos());
    }

    void MyClass::mouseMoveEvent(QMouseEvent* event)
    {
        m_flickable->handleMouseMove(event->pos());
    }

    void MyClass::mouseReleaseEvent(QMouseEvent* event)
    {
        if (event->button() == Qt::LeftButton)
            m_flickable->handleMouseRelease(event->pos());
    }

 If the scroll range is likely to change (as lists often do!), connect the QScrollBar::rangeChanged signal
 to the rangeChange() slot or call the setMinimumValue()/setMaximumValue() functions.

 The callbacks should be used to get and set the current scroll values (eg from QScrollBar).

 The setStretchValue() defines how much of the area can be stretched/bounced. It is the callers
 responsibility to ensure that the area has the sufficient blank padding at the top and bottom.

 There are various configurable values to fine tune the kinectic scrolling in the source file as defines.
 */

class OLFlickAndDragInfoProvider
    {
public:
    // scroll area handling
    virtual int scrollValue() = 0;
    virtual void setScrollValue(int) = 0;

    // event handling
    virtual void handleClick(const QPoint&) = 0;
    virtual void handleLongPress(const QPoint&) =0;
    virtual void handleLeftSwipe() = 0;
    virtual void handleRightSwipe() = 0;
    };

class OLFlickAndDrag  : public QObject
    {
    Q_OBJECT

public:
    OLFlickAndDrag(OLFlickAndDragInfoProvider& observer, int min, int max, int stretch, QObject *parent=0);
    ~OLFlickAndDrag();

    void setMinMaxScrollValue(int min, int max);
    void setStretchValue(int stretch);

    void handleMousePress(const QPoint& pos);
    void handleMouseMove(const QPoint& pos);
    void handleMouseRelease(const QPoint& pos);

public slots:
    void rangeChange(int min, int max);

private slots:
    void handleTimerEvent();

private:
    void reset();
    void handleSnap(int min, int max, int target);
    bool handleLeftOrRightSwipe(int xdelta, int ydelta) const;

private:
    enum FlickState
    {
        Idle = 0,
        Pressed,
        Dragging,
        AutoScrolling,
        Stopped,
        StretchedTop,
        StretchedBottom
    };

    enum DragDirection
    {
        UnknownDirection = 0,
        MovingUp,
        MovingDown
    };

private:
    OLFlickAndDragInfoProvider& m_observer;
    int m_min;
    int m_max;
    int m_scrollValue;
    FlickState m_state;
    DragDirection m_direction;
    QTimer *m_timer;
    QTime m_stopwatch;
    QTime m_totalTime;
    QPoint m_startPos;
    QPoint m_lastMovePos;
    int m_lastDiffs[3];
    qreal m_speed;
    int m_stretch;
    };

#endif // __OLFLICKANDDRAG_H__
